package top.easyblog.core.service;

import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import top.easyblog.common.enums.CompressionCodeStatus;
import top.easyblog.common.request.QueryCompressCodesRequest;
import top.easyblog.core.atomic.AtomicCompressCodeService;
import top.easyblog.core.config.CustomConfiguration;
import top.easyblog.core.dao.auto.mapper.CompressionCodeMapper;
import top.easyblog.core.dao.auto.model.CompressionCode;
import top.easyblog.core.support.keygen.SequenceGenerator;
import top.easyblog.core.support.mybatis.BatchOperateSupport;
import top.easyblog.core.support.queue.CompressionCodeBlockingQueue;
import top.easyblog.core.util.AssertUtil;
import top.easyblog.core.util.ConversionUtils;

import javax.annotation.PostConstruct;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static top.easyblog.common.constant.Constants.COMMON_ORDER_DIR_ASC;
import static top.easyblog.common.constant.Constants.COMPRESSION_CODE_ORDER_FIELD_ID;

/**
 * @author: frank.huang
 * @date: 2023-12-18 23:10
 */
@Slf4j
@Service
public class CompressCodeService {

    @Autowired
    private AtomicCompressCodeService atomicCompressCodeService;

    @Autowired
    private SequenceGenerator sequenceGenerator;

    @Autowired
    private CustomConfiguration customConfiguration;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Getter
    private CompressionCodeBlockingQueue queue;


    @PostConstruct
    public void init() {
        queue = new CompressionCodeBlockingQueue(customConfiguration, this, redisTemplate);
    }


    public String getAvailableCompressCode() {
        String code = queue.poll();
        log.info("从Redis队列中获取到压缩码：{}", code);
        if (StringUtils.isNotBlank(code)) {
            return code;
        }

        log.info("生成新的压缩码.....");
        return Optional.ofNullable(getLatestAvailableCompressCode()).map(CompressionCode::getCompressionCode).orElseGet(() -> {
            //兜底逻辑
            if (customConfiguration.isCompressCodeBatchEnable()) {
                generateBatchCompressionCodes(1);
            }
            return Objects.requireNonNull(this.getLatestAvailableCompressCode()).getCompressionCode();
        });
    }


    private CompressionCode getLatestAvailableCompressCode() {
        return Iterables.getFirst(getCompressCodeList(NumberUtils.INTEGER_ONE, NumberUtils.INTEGER_ZERO), null);
    }


    public List<CompressionCode> getCompressCodeList(Integer limit, Integer offset) {
        return atomicCompressCodeService.queryListByRequest(QueryCompressCodesRequest.builder().status(CompressionCodeStatus.AVAILABLE.getValue()).limit(limit).offset(offset).orderCause(COMPRESSION_CODE_ORDER_FIELD_ID).orderDir(COMMON_ORDER_DIR_ASC).build());
    }

    /**
     * 批量生成一批压缩码
     */
    public void generateBatchCompressionCodes(int batchGenerateNum) {
        List<CompressionCode> compressionCodeList = Lists.newArrayList();
        Set<String> sets = Collections.synchronizedSet(new HashSet<>());

        for (int i = 0; i < batchGenerateNum; ) {
            long sequence = sequenceGenerator.generate();
            String encode62Code = "";
            final String code = (encode62Code = ConversionUtils.encode62(sequence))
                    .substring(encode62Code.length() - 6);

            CompressionCode existCode = atomicCompressCodeService.queryByCode(code);
            if (Objects.nonNull(existCode) || sets.contains(code)) {
                log.info("压缩码 {} 已经存在，舍弃。", code);
            } else {
                CompressionCode compressionCode = generateCompressionCode(sequence, code);
                compressionCodeList.add(compressionCode);
                sets.add(code);
                i++;
            }
        }

        atomicCompressCodeService.batchInsert(compressionCodeList);
    }


    private CompressionCode generateCompressionCode(long sequence, String code) {
        CompressionCode compressionCode = new CompressionCode();
        compressionCode.setSequenceValue(String.valueOf(sequence));
        compressionCode.setCompressionCode(code);
        compressionCode.setStatus(CompressionCodeStatus.AVAILABLE.getValue());
        compressionCode.setDeleted(false);
        compressionCode.setStrategy("sequence");
        return compressionCode;
    }


    public void updateStatus(String code, Byte status) {
        AssertUtil.isTrue(StringUtils.isNotBlank(code), "压缩码不能为空");
        AssertUtil.isTrue(Objects.nonNull(status), "状态不能为空");
        CompressionCode compressionCode = atomicCompressCodeService.queryByCode(code);
        AssertUtil.notNull(compressionCode, "未找到指定的压缩码：" + code);

        CompressionCode newCode = new CompressionCode();
        newCode.setId(compressionCode.getId());
        newCode.setStatus(status);
        atomicCompressCodeService.updateByPrimaryKey(newCode);
    }


    public void batchUpdateStatus(List<String> codes, Byte status) {
        List<List<String>> partitions = Lists.partition(codes, 10000);
        for (List<String> list : partitions) {
            atomicCompressCodeService.batchUpdateStatus(list, status);
            try {
                //等待一会儿，避免把数据库搞崩了
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }


    public long countByStatus(CompressionCodeStatus status) {
        AssertUtil.notNull(status, "压缩码状态不能为空");
        return atomicCompressCodeService.countByStatus(status.getValue());
    }

}
